/* 
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Project: MyMANET - A Platform to Build Customized MANET's
Developers: Ashwin Paranjpe, Santosh Vempala
Georgia Institute of Technology, Atlanta, USA
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdint.h>	/*for uint32_t*/
#include<sys/socket.h>
#include<features.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<net/if.h>
#include<net/ethernet.h>

#define DEBUG 0

#define DST_ETHER_ADDR	"FF:FF:FF:FF:FF:FF"

#define BUFFER_LEN 512
#define MANIFOLD_UPDATE_TYPE 0x3434
#define HB_INTERVAL 5

int g_additional_len = 0;
uint8_t g_mac[6];

int string_etheraddr(const u_char *asc, char *addr)
{
	int cnt;

	for (cnt = 0; cnt < 6; ++cnt) {
		unsigned int number;
		char ch;

		ch = tolower (*asc++);
		if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f'))
			return 1;
		number = isdigit (ch) ? (ch - '0') : (ch - 'a' + 10);

		ch = tolower(*asc);
		if ((cnt < 5 && ch != ':') || (cnt == 5 && ch != '\0' && !isspace (ch))) {
			++asc;
			if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f'))
				return 1;
			number <<= 4;
			number += isdigit (ch) ? (ch - '0') : (ch - 'a' + 10);
			ch = *asc;
			if (cnt < 5 && ch != ':')
				return 1;
		}

		/* Store result.  */
		addr[cnt] = (unsigned char) number;

		/* Skip ':'.  */
		++asc;
	}

	return 0;
}

int CreateRawSocket(int protocol_to_send)
{
	int rawsock;

	if((rawsock = socket(PF_PACKET, SOCK_RAW, htons(protocol_to_send)))== -1)
	{
		perror("Error creating raw socket: ");
		exit(-1);
	}

	return rawsock;
}

int BindRawSocketToInterface(char *device, int rawsock, int protocol)
{

	struct sockaddr_ll sll;
	struct ifreq ifr;
	unsigned char *mac;

	bzero(&sll, sizeof(sll));
	bzero(&ifr, sizeof(ifr));

	/*Copy device name*/
	strncpy((char *)ifr.ifr_name, device, IFNAMSIZ);

	/*Get hardware address */
	if (ioctl(rawsock, SIOCGIFHWADDR, &ifr) < 0)
	{ 
		printf("ioctl Error: SIOCGIFHWADDR");
		exit(1); 
	}

	memset(g_mac, 0, 6);
	memcpy(g_mac, ifr.ifr_hwaddr.sa_data, 6); 
#if DEBUG
	printf("\nMy Mac is : %x:%x:%x:%x:%x:%x\n", g_mac[0], g_mac[1], g_mac[2], g_mac[3], g_mac[4], g_mac[5]);
#endif

	/*Get the Interface Index  */
	if((ioctl(rawsock, SIOCGIFINDEX, &ifr)) == -1)
	{
		printf("Error getting Interface index !\n");
		exit(-1);
	}

	/*Bind Raw socket to this interface*/
	sll.sll_family = AF_PACKET;
	sll.sll_ifindex = ifr.ifr_ifindex;
	sll.sll_protocol = htons(protocol); 


	if((bind(rawsock, (struct sockaddr *)&sll, sizeof(sll)))== -1)
	{
		perror("Error binding raw socket to interface\n");
		exit(-1);
	}


	return 1;

}


int SendRawPacket(int rawsock, unsigned char *pkt, int pkt_len)
{
	int sent= 0;
#if DEBUG
	printf("Packet len: %d\n", pkt_len);
#endif

	if((sent = write(rawsock, pkt, pkt_len)) != pkt_len)
	{
		/* Print Error */
		printf("Could only send %d bytes of packet of length %d\n", sent, pkt_len);
		return 0;
	}

	return 1;


}

unsigned char* CreateEthernetPacket(uint8_t src_mac[6], char *dst_mac, int protocol, char ip_address[15], char node_name[10], uint8_t is_gw, uint8_t num_of_dns)
{
	struct ethhdr *ethernet_header;
	char *buf, *packet;
	int file_empty = 1;
	int tx_file_empty = 1;

	FILE *fp = NULL, *fp2 = NULL;
	char line[80];
	uint32_t ip_long=0, ip_long_nl=0;
	//struct ether_addr *dst_mac_temp = NULL, *mac_temp = NULL;
	char dst_mac_temp[6], mac_temp[6];

	int i=0;
	FILE *fp_dns = NULL;
	
	fp = fopen("/proc/rxstats", "r");	

	if(fp == NULL){
		printf("\nRX Proc file could not be read\n");
		exit(1);	
	}

	fp2 = fopen("/proc/txstats", "r");	

	if(fp2 == NULL){
		printf("\nTX Proc file could not be read\n");
		exit(1);	
	}

	ethernet_header = (struct ethhdr *)malloc(sizeof(struct ethhdr) + (sizeof(char) * BUFFER_LEN));
	if(ethernet_header == NULL){
		printf("\nMalloc failed\n");
		exit(1);
	}

	memset(ethernet_header, 0, sizeof(struct ethhdr) + (sizeof(char) * BUFFER_LEN));

	/*Copy the Src mac addr. Already in Byte order, not in Ascii.*/
	memcpy(ethernet_header->h_source, src_mac, 6);

	/*Copy the Dst mac addr. Convert to NB order*/
	if(string_etheraddr(dst_mac, dst_mac_temp) != 0){
		printf("\n1: Error in string_etheraddr\n");
		exit(1);
	}
	memcpy(ethernet_header->h_dest, dst_mac_temp, 6);

	/*Copy the protocol*/
	ethernet_header->h_proto = htons(protocol);

	/*Send the header back */
	buf = (char *) ((char *)ethernet_header + sizeof(struct ethhdr));

	/*Pack this hosts' details. MAC address, IP Address, Node Name and the TX stats.*/
	memcpy(buf, src_mac, 6);
	buf = buf + (6 * sizeof(char));
	g_additional_len +=  (6 * sizeof(char));

	ip_long = inet_addr(ip_address);
	ip_long_nl = htonl(ip_long);

	memcpy(buf, (void *)&ip_long_nl, sizeof(uint32_t));
	buf = buf + (sizeof(uint32_t));
	g_additional_len +=  (sizeof(uint32_t));

	memcpy(buf, node_name, 10);
	buf = buf + (10 * sizeof(char));
	g_additional_len +=  (10 * sizeof(char));

	memcpy(buf, &is_gw, 1);
	buf = buf + (1 * sizeof(char));
	g_additional_len +=  (1 * sizeof(char));

	if(is_gw){	/*Check whether this is a GW.*/
		memcpy(buf, &num_of_dns, 1);	/*Note: This assumes that no. of DNS IPs does not exceed 9.*/
		buf = buf + (1 * sizeof(char));
		g_additional_len +=  (1 * sizeof(char));

		if(num_of_dns){
			if((fp_dns=fopen("../../config/resolv.conf", "r"))!=NULL){

				for(i=0;i<num_of_dns; i++){
					char ip_address_dns[16]="";
					char temp[20]="";
					uint32_t ip_long_dns=0, ip_long_dns_nl=0;

					fscanf(fp_dns, "%s %s", temp, ip_address_dns);
					ip_long_dns = inet_addr(ip_address_dns);
					ip_long_dns_nl = htonl(ip_long_dns);

					memcpy(buf, (void *)&ip_long_dns_nl, sizeof(uint32_t));
					buf = buf + (sizeof(uint32_t));
					g_additional_len +=  (sizeof(uint32_t));
				}
				fclose(fp_dns);
			}
		}
	}

	tx_file_empty = 1;

	while((fgets(line, 80, fp2) != NULL) && (g_additional_len < BUFFER_LEN)){
#if DEBUG
		printf("%s\n", line);		
#endif
		char *num_of_pkts_string = strtok(line, " ");
		char *session_id_string = strtok(NULL, " ");

		uint32_t num_of_pkts = atoi(num_of_pkts_string);
		uint32_t session_id = atoi(session_id_string);
		uint32_t num = htonl(num_of_pkts); 
		uint32_t session = htonl(session_id); 

		memcpy(buf, (void *)&num, sizeof(uint32_t));
		buf = (buf + sizeof(uint32_t));
		g_additional_len += (sizeof(uint32_t));

		memcpy(buf, (void *)&session, sizeof(uint32_t));
		buf = (buf + sizeof(uint32_t));
		g_additional_len += (sizeof(uint32_t));

		memset(line, 0, 80);
		tx_file_empty = 0;
	}
	/*END*/

	file_empty = 1;

	while((fgets(line, 80, fp) != NULL) && (g_additional_len < BUFFER_LEN)){
#if DEBUG
		printf("%s\n", line);		
#endif
		file_empty = 0;
		char *mac = strtok(line, " ");
		char *num_of_pkts_string = strtok(NULL, " ");
		char *session_id_string = strtok(NULL, " ");

		uint32_t num_of_pkts = atoi(num_of_pkts_string);
		uint32_t session_id = atoi(session_id_string);
		uint32_t num = htonl(num_of_pkts); 
		uint32_t session = htonl(session_id); 

#if DEBUG
		printf("mac : %s num of pkts : %s sizeof(uint32_t): %d, num_of_pkts : %d session : %d\n", 
				mac, num_of_pkts_string, sizeof(uint32_t), num_of_pkts, session_id);
#endif
		if(string_etheraddr(mac, mac_temp) != 0){
			printf("\n2: Error in string_etheraddr\n");
			exit(1);
		}
		memcpy(buf, mac_temp, 6);
		buf = buf + (6 * sizeof(char));
		g_additional_len +=  (6 * sizeof(char));

		memcpy(buf, (void *)&num, sizeof(uint32_t));
		buf = (buf + sizeof(uint32_t));
		g_additional_len += (sizeof(uint32_t));

		memcpy(buf, (void *)&session, sizeof(uint32_t));
		buf = (buf + sizeof(uint32_t));
		g_additional_len += (sizeof(uint32_t));

		memset(line, 0, 80);
	}

	if(fp)
		fclose(fp);
	if(fp2)
		fclose(fp2);

	if((file_empty == 1) || (tx_file_empty == 1)){ 	/*Dont send this packet. The TX or RX stat file(s) is/are empty. Try later.*/

		if(ethernet_header!=NULL)
			free(ethernet_header);

		return NULL;
	}

	packet =(char *) ethernet_header;

	return ((unsigned char *)packet);


}


char* get_node_ip(int *len)
{
	chdir ("../../config");
	char s[100];
	char ip_add[20];
	char *ip;
	memset (&ip_add, '\0', 20);
	FILE *stream;
	memset (&s, '\0', 100);
	if((stream = fopen ("lan_setup.conf", "r")) != (FILE *)0) {
		while((fgets(s, 100, stream)) != (char *)0 ) {
	//		printf ("\n %s", s);
		}
	} else {
		
	}
	close (stream);
	chdir ("../tools/raw");   
	char * ret = strtok (s, " ");
	ret = strtok (NULL, " ");
	ip = strtok (NULL, " ");
	strncpy (ip_add, ip, 20);
	*len = strlen(ip_add);
	return ip;
}



/* argv[1] is the device e.g. eth0    */

main(int argc, char **argv)
{

	int raw;
	unsigned char *packet;
	int packet_len;
	int tmp=0;

	if(argc!=6){
		printf("\nUsage: ./inject [INTERFACE] [IP_ADDRESS] [NODE_NAME] [IS_GW] [NUM_OF_DNS]\n\n");
		exit(0);
	}

	/*Create Raw socket*/
	raw = CreateRawSocket(MANIFOLD_UPDATE_TYPE);

	/*Bind raw socket to interface*/
	BindRawSocketToInterface(argv[1], raw, MANIFOLD_UPDATE_TYPE);

	char *ret_ip_ptr;
	char ret_ip[20];
	char ip_add[20];
	int ip_len;		

	while(1){

		memset (&ret_ip, '\0', 20);
		memset (&ip_add, '\0', 20);
		strncpy (ret_ip, ret_ip_ptr, ip_len);

		sleep(HB_INTERVAL);

		tmp = 0;

		g_additional_len = 0;

		/*Create Ethernet header*/
		packet = CreateEthernetPacket((unsigned char *)g_mac, DST_ETHER_ADDR, MANIFOLD_UPDATE_TYPE, argv[2], argv[3], atoi(argv[4]), atoi(argv[5]));


		if(packet == NULL)
			continue;

		packet_len = sizeof(struct ethhdr) + (sizeof(char) * g_additional_len);

#if DEBUG
		printf("\nPacket is :\n");
		while (tmp < (packet_len)){
			printf("%x ", (int) (*(packet + tmp)));
			tmp++;
		}
		printf("\n\n");
#endif



		if(!SendRawPacket(raw, packet, packet_len))
		{
			perror("Error sending packet");
		}
		else{
#if DEBUG
			printf("Packet sent successfully\n");
#endif
		}

		free(packet);
	}


	close(raw);

	return 0;
}

